Conversation
|
대량의 로그 파일(100GB)을 처리해서 특정 패턴의 라인만 추출해야 한다면, 컴프리헨션/제너레이터/이터레이터 중 무엇을 선택할까요? 각각의 메모리 사용량 차이를 예상해보면 좋을 것 같아요. |
| # 리스트 컴프리헨션 활용 | ||
| squares_comp = [i * i for i in range(10) if i % 2 == 0] | ||
| print(squares_comp) # [0, 4, 16, 36, 64] | ||
| ``` |
There was a problem hiding this comment.
2중, 3중 중첩된 리스트 컴프리헨션을 작성해본 경험이 있나요? 어느 시점부터 일반 for문으로 바꾸는 것이 더 나을까요?
There was a problem hiding this comment.
명확한 기준이 있지는 않아서 팀 규칙에 따라 작성하면 될것 같습니다.
개인적으로는 (2중 컴프리핸션 + If문) 부터 for문으로 바꾸는 편입니다.
|
|
||
| 일반적인 상황에서 List comprehension 의 동작이 유의미하게 빠르다. | ||
| 아래 예제를 통해 간단하게 확인이 가능하다. | ||
| > 정확히 설명하자면 인터프리터 모드인 경우에 유의미하게 빠르다. |
There was a problem hiding this comment.
자료에서 "인터프리터 모드에서 유의미하게 빠르다"고 했는데, 컴파일된 코드에서는 차이가 없을까요? 그리고 왜 컴프리헨션이 더 빠른지 내부 동작 원리를 설명할 수 있나요?
There was a problem hiding this comment.
Cpython 과 같이 컴파일된 코드에선 차이가 없는걸로 알고 있습니다.
리스트를 생성하는 코드에서 list.append() 바이트 코드 레벨에서 성능 차이가 발생하는걸로 알고 있습니다.
|
|
||
| # 실행 시간 측정 | ||
| for_loop_time = timeit.timeit(stmt=for_loop_code, setup=setup_code, number=1) | ||
| comprehension_time = timeit.timeit(stmt=comprehension_code, setup=setup_code, number=1) |
There was a problem hiding this comment.
1억 개의 숫자를 처리해야 한다면 리스트 컴프리헨션이 최선일까요? 제너레이터 표현식 (x**2 for x in range(100000000))과 비교했을 때 어떤 차이가 있을까요?
There was a problem hiding this comment.
해당 케이스라면 제너레이터를 쓰는게 맞을것 같습니다.
리스트 컴프리헨션은 모두 메모리에 올려서 한번에 처리해 메모리 이슈가 생길것 같습니다.
| my_gen = number_generator(3) | ||
| print(next(my_gen)) # Generator starts, 1 출력 | ||
| print(next(my_gen)) # 1 yielded, 2 출력 | ||
| print(next(my_gen)) # 2 yielded, 3 출력 |
There was a problem hiding this comment.
예제의 my_gen을 한번 순회한 후 다시 사용하려면 어떻게 해야 할까요? 제너레이터가 exhausted되는 특성이 실제 코딩에서 어떤 버그를 유발할 수 있을까요?
There was a problem hiding this comment.
제너레이터는 재사용(reset)이 불가능한것으로 알고 있어서 다시 사용하려면 재할당이 필요할것 같습니다.
데이터 검증 같은 호출에서 exhausted 되어 예상치 못한 버그를 일으킬 수 있습니다.
|
|
||
| #### 이터레이터 vs 이터러블 | ||
| - **이터러블(Iterable)**: `__iter__()` 메서드를 가진 객체 (리스트, 튜플, 문자열 등) | ||
| - **이터레이터(Iterator)**: `__iter__()`와 `__next__()` 메서드를 모두 가진 객체 |
There was a problem hiding this comment.
iter()와 next()를 구현해야 하는 이유는 뭘까요? 그냥 리스트를 반환하면 안 되는 특별한 상황이 있을까요?
There was a problem hiding this comment.
리스트로 반환할 경우 리스트 전체를 메모리에 올려야해서 Lazy하게 처리하기 위해 사용합니다.
| - 주로 개발자가 디버깅 목적으로 사용하는, 객체를 **명확하게 식별**할 수 있는 **공식적인** 문자열 표현을 반환하는 데 사용된다. `eval(repr(obj)) == obj`가 성립하는 것을 목표로 한다. | ||
| - `__eq__(self, other)` | ||
| - `==` 연산자를 사용하여 두 객체의 **내용이 같은지** 비교할 때 호출된다. | ||
| - 이 메서드를 구현하지 않으면, 기본적으로 두 객체의 메모리 주소를 비교한다. |
There was a problem hiding this comment.
__eq__만 구현하고 __hash__를 구현하지 않으면 어떤 문제가 생길까요?
There was a problem hiding this comment.
해시 불가능해서 에러가 발생합니다.
Hashable Contract 로 두 객체가 같다면, 두 객체의 해시값도 반드시 같아야 한다 때문에 그렇군요..?
처음 알았네요!
| return result | ||
| return wrapper | ||
|
|
||
| @measure_time |
There was a problem hiding this comment.
여러 데코레이터를 겹쳐서 사용할 때(@decorator1 @decorator2 @func) 실행 순서는 어떻게 될까요?
There was a problem hiding this comment.
func = decorator1(decorator2(func))와 동일합니다.
| import time | ||
|
|
||
| def measure_time(func): | ||
| def wrapper(*args, **kwargs): |
There was a problem hiding this comment.
예제의 wrapper 함수는 원본 함수의 메타데이터(이름, docstring 등)를 잃어버려요. @functools.wraps(func)를 사용하지 않으면 어떤 문제가 생길까요?
There was a problem hiding this comment.
사용하지 않을 경우 디버깅하는데 있어서 원본 함수가 보이지 않고 Typehint 등이 사라질 수 있습니다
학습 내용 요약
파이썬의 고급 기능 4가지를 학습했습니다.
컴프리헨션으로 반복문을 한 줄로 압축하는 방법, 언패킹과 제너레이터로 메모리 효율적인 데이터 처리, 매직 메서드로 클래스 커스터마이징, 데코레이터로 함수 기능 확장하는 방법을 배웠습니다.
핵심 개념
제너레이터와 이터레이터의 차이점과 활용법이 가장 중요하다고 생각합니다.
제너레이터는 yield 키워드를 사용해 메모리 효율적으로 대용량 데이터를 처리할 수 있게 해주며, 이터레이터는
__iter__()와__next__()메서드를 구현해 순회 가능한 객체를 만드는 기반이 됩니다.특히 지연 평가(Lazy Evaluation) 개념을 이해하면 메모리 사용량을 크게 줄일 수 있습니다.
실습 예제
참고 자료
체크리스트